#!/usr/bin/perl

=script: count_watch.pl
| Description:  Gets WATCH and LIVE counts from cam4.com every x minutes and
|               and displays the changes between each iteration. Useful for
|               quickly catching large spikes/dips in site usage.
|
| Changelog:
|   6/15/2013 - Initial write by Ernie Teem.
|   6/16/2013 - Changes by Gary Locke;
|                   > Uses strict pragma with warnings;
|                   > Loops internally (no bash loop required);
|                   > Prints changes in colors;
|                   > Takes interval (in minutes) as only argument;
|                   > Gets count faster, from /login instead of index.php;
|                   > Handles connection failure to cam4.com.
|   6/18/2013 - Bug fixes;
|                   > Change percentage now calculated using previous counts;
|                   > Error handling improved for bad connection to cam4.com;
|                   > Added status column for quick review of results history;
|                   > Takes argument for drop amount to trigger alert status.
|   6/19/2013 - Bug fixes;
|                   > Added column for page load time;
|                   > Gets index.php again, so load time value is more useful.
|   6/26/2012 - Added terminal bell for ALERT result.
=cut

use strict;
use warnings;
use IO::Handle;
use POSIX qw{strftime};
require LWP::UserAgent;

my $Red = "\e[31m";
my $Green = "\e[32m";
my $Cyan = "\e[36m";
my $Yellow = "\e[33m";
my $Reset = "\e[0m";

my $ua = LWP::UserAgent->new;
$ua->timeout(10);

my $wait = 120;
if ($ARGV[0]) {
    if ($ARGV[0] !~ /^[0-9]+$/) {
        print $Yellow."Argument one must be a number.\n\n".$Reset;
        &displayHelp;
    } else {
        $wait = $ARGV[0]*60;
    }
}

my $max_drop = 5;
if ($ARGV[1]) {
    if ($ARGV[1] !~ /^[0-9]+$/) {
        print $Yellow."Argument two must be a number.\n\n".$Reset;
        &displayHelp;
    } else {
        $max_drop = $ARGV[1];
    }
}

my ($c,$unixtime_before,$unixtime_after,$load_time,$w_diff,$w_diff_perc,$w_perc_color,$l_diff,$l_diff_perc,$l_perc_color,$w_prev,$l_prev,$w_cur,$l_cur,$line,$page,$status,@source,@counts);

print $Cyan."\n    For every ".$Red.$wait/60 ."min".$Cyan." interval, you will be alerted of a ".$Red.$max_drop."%".$Cyan." drop in any value.".$Reset."\n";


my $iter = 0;
for(;;) {
    undef $page;
    undef @source;
    undef @counts;
    my $time = strftime("\%H:\%M:\%S", localtime);
    $unixtime_before = time;
    $page = $ua->get('http://www.cam4.com/');
    $unixtime_after = time;
    unless ($page->is_success) {
        &tryAgain("Could not connect to cam4.com.",9);
        next;
    }
    @source = split(/\n/,$page->content);
    foreach $line (@source) {
        if ($line =~ /([0-9]+\ WATCH|[0-9]+\ LIVE)/) {
            $line =~ s/[^0-9]+//g;
            push(@counts,$line);
        }
    }
    if (!defined $counts[0] && !defined $counts[1]) {
        &tryAgain("Connected, but couldn't retrieve counts from cam4.com.",5);
        next;
    } elsif ($counts[0] =~ /^[[:blank:]]*$/ || $counts[1] =~ /^[[:blank:]]*$/) {
        &tryAgain("Connected, but retrieved invalid (empty?) count values from cam4.com.",5);
        next;
    } else {
        $w_cur   = $counts[0];
        $l_cur       = $counts[1];
        $load_time  = $unixtime_after-$unixtime_before."s";
    }
    if ($iter eq 0) {
        print "\n\t\tLtime\tWcnt\tWchg\tWdiff\tLcnt\tLchg\tLdiff\tStatus\n";
        $w_diff = $w_perc_color = $l_diff = $l_perc_color = $status = "n/a";
    } else {
        ($w_diff,$w_diff_perc)  = &getDiffs($w_prev,$w_cur);
        ($l_diff,$l_diff_perc)  = &getDiffs($l_prev,$l_cur);
        $w_perc_color           = &formatValues($w_diff_perc);
        $l_perc_color           = &formatValues($l_diff_perc);
        $status                 = &setStatus($w_diff_perc,$l_diff_perc,$max_drop);
    }
    print "$time\t$load_time\t$w_cur\t$w_diff\t$w_perc_color\t$l_cur\t$l_diff\t$l_perc_color\t$status\n";

    if ($status =~ /ALERT/) {
        for (my $j=0;$j<8;$j++) {
            STDOUT->printflush("\a");
            sleep(1);
        }
    }

    $w_prev = $w_cur;
    $l_prev = $l_cur;
    sleep($wait);
    $iter++;
}

sub displayHelp {
    print "Usage: (defaults given in example)\n";
    print "\t./cam4_count_watch.pl 3 5\n";
    print "\t                      | |\n";
    print "\t                      | -- Percentage of drop in watch/live count (within specified interval) before alert.\n";
    print "\t                      ------ Interval of count checks (.\n\n";
    exit;
}

sub getDiffs {
    my $diff_perc;
    my $oldcount    = $_[0];
    my $newcount    = $_[1];
    my $diff        = ($newcount - $oldcount);
    if ($oldcount == 0) {
        $diff_perc  = "n/a";
    } else {
        $diff_perc  = $diff / $oldcount;
        $diff_perc  = sprintf '%.2f%%', 100 * $diff_perc;
    }
    return ($diff,$diff_perc);
}

sub formatValues {
    my $value = $_[0];
    if ($value =~ /-/) {
        $value = $Red.$value.$Reset;
    } elsif ($value ne '0.00%') {
        $value = $Green.$value.$Reset;
    }
    return $value;
}

sub tryAgain {
    my $retry_msg = $_[0];
    my $delay = $_[1];
    STDOUT->printflush($retry_msg." Trying again in ".$delay." seconds...");
    for ($c = $delay;$c gt 0; $c--) {
        STDOUT->printflush("\b" x 12);
        STDOUT->printflush($c." seconds...");
        sleep 1;
    }
    STDOUT->printflush("\b" x 12 . "0 seconds...\n");
    sleep 1;
}

sub setStatus {
    my @changes = ($_[0],$_[1]);
    my $max = $_[2];
    my $alert = 0;
    my $status_msg;
    foreach my $value (@changes) {
        if ($value =~ /-/) {
            $value =~ s/[-%]//g;
            if ($value >= $max) {
                $status_msg = $Red."ALERT".$Reset;
                last;
            } elsif ($value >= $max/2) {
                $alert = 1;
                $status_msg = $Yellow."WARN".$Reset;
            } elsif ($alert eq 0) {
                $status_msg = $Green."OK".$Reset;
            }
        } elsif ($alert eq 0) {
            $status_msg = $Green."OK".$Reset;
        }
    }
    return $status_msg;
}
